use super::chat_loop::ChatLoop;
use crate::models::{ChatMessage, ProjectConfig};
use anyhow::{Context, Result};
use reqwest::Client;
use serde::{Deserialize, Serialize};
use serde_json::Value;

#[derive(Deserialize, Serialize, Debug)]
pub struct Usage {
    pub prompt_tokens: u32,
    pub completion_tokens: u32,
    pub total_tokens: u32,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct FunctionCall {
    pub name: String,
    pub arguments: String,
}

#[derive(Deserialize, Serialize, Debug)]
pub struct ToolCall {
    pub id: String,
    pub function: FunctionCall,
}

#[derive(Serialize, Debug, Clone)]
pub struct ResponseFormat {
    #[serde(rename = "type")]
    pub r#type: String,
}

#[derive(Deserialize, Serialize, Debug)]
pub struct Message {
    pub content: Option<String>,
    pub tool_calls: Option<Vec<ToolCall>>,
}

#[derive(Deserialize, Serialize, Debug)]
pub struct Choice {
    pub message: Message,
    pub finish_reason: Option<String>,
}

#[derive(Serialize, Debug, Clone)]
pub struct ChatRequest {
    pub messages: Vec<ChatMessage>,
    pub model: String,
    pub max_tokens: Option<u32>,
    pub temperature: Option<f32>,
    pub response_format: ResponseFormat,
    pub tools: Option<Value>,
    pub tool_calls: Option<Vec<String>>,
}

#[derive(Deserialize, Serialize, Debug)]
pub struct ChatResponse {
    pub id: String,
    pub choices: Vec<Choice>,
    pub usage: Usage,
}

pub struct OpenAIClient {
    base_url: String,
    api_key: String,
    client: Client,
}

impl OpenAIClient {
    pub fn new(api_key: &str, base_url: &str) -> Self {
        Self {
            base_url: base_url.to_string(),
            api_key: api_key.to_string(),
            client: Client::new(),
        }
    }

    pub async fn send_request(&self, request: &ChatRequest) -> Result<ChatResponse> {
        let url = format!("{}/chat/completions", self.base_url);
        let response = self
            .client
            .post(&url)
            .header("Authorization", format!("Bearer {}", self.api_key))
            .header("Content-Type", "application/json")
            .json(request)
            .send()
            .await?;

        if !response.status().is_success() {
            anyhow::bail!("HTTP Error: {}", response.status());
        }

        let response_text = response
            .text()
            .await
            .context("Failed to read response as text")?;

        match serde_json::from_str(&response_text) {
            Ok(body) => Ok(body),
            Err(e) => {
                eprintln!(
                    "Failed to parse JSON response. Response text: {}",
                    response_text
                );
                Err(e.into())
            }
        }
    }

    pub async fn chat(
        &self,
        request: &ChatRequest,
        context: &Option<ProjectConfig>,
    ) -> Result<ChatResponse> {
        ChatLoop::run(self, (*request).clone(), context).await
    }
}
